package cn.trigram.validation;


import java.lang.reflect.Method;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import javax.validation.Validation;
import javax.validation.Validator;
import javax.validation.executable.ExecutableValidator;
import org.apache.commons.collections4.CollectionUtils;


/**
 * 使用hibernate-validator 做复杂校验工具
 *
 * @version 1.0.0
 * @date 2022/08/03 17:09:24
 * @since 1.0.0
 */
public class ValidateUtil {

    private static final Validator VALIDATOR = Validation.byDefaultProvider()
            .configure()
            .ignoreXmlConfiguration()
            .buildValidatorFactory()
            .getValidator();

    /**
     * 验证bean，包装异常信息
     *
     * @param <T>    被验证bean的类型
     * @param bean   被验证的bean
     * @param groups 验证组
     * @return {@link #wrapConstraintViolations(Set)}
     */
    public static <T> List<String> validateBeanWrapConstraintViolations(T bean, Class<?>... groups) {

        final Set<ConstraintViolation<T>> constraintViolations = validateBean(bean, groups);
        return wrapConstraintViolations(constraintViolations);
    }

    /**
     * 验证bean，并抛出异常
     *
     * @param <T>    被验证bean的类型
     * @param bean   被验证的bean
     * @param groups 验证组
     */
    public static <T> void validateBeanThrowException(T bean, Class<?>... groups) {

        final Set<ConstraintViolation<T>> constraintViolations = validateBean(bean, groups);
        if (CollectionUtils.isNotEmpty(constraintViolations)) {
            throw new ConstraintViolationException(constraintViolations);
        }
    }

    /**
     * 验证bean，不抛出异常
     *
     * @param <T>    被验证bean的类型
     * @param bean   被验证的bean
     * @param groups 验证组
     */
    public static <T> Set<ConstraintViolation<T>> validateBean(T bean, Class<?>... groups) {
        if (Objects.isNull(groups)) {
            throw new IllegalArgumentException("groups can't null. Instead of empty groups");
        }
        return VALIDATOR.validate(bean, groups);
    }

    /**
     * 验证方法入参，不抛出异常
     *
     * @param <T>             所属对象的类型
     * @param object          方法所属对象
     * @param method          方法
     * @param parameterValues 方法入参值
     * @param groups          校验组
     */
    public static <T> Set<ConstraintViolation<T>> validateMethod(
            T object, Method method, Object[] parameterValues, Class<?>... groups) {
        if (Objects.isNull(groups)) {
            throw new IllegalArgumentException("groups can't null. Instead of empty groups");
        }
        ExecutableValidator executableValidator = VALIDATOR.forExecutables();
        return executableValidator.validateParameters(object, method, parameterValues, groups);
    }

    /**
     * 验证方法入参，抛出异常
     *
     * @param <T>             所属对象的类型
     * @param object          方法所属对象
     * @param method          方法
     * @param parameterValues 方法入参值
     * @param groups          校验组
     */
    public static <T> void validateMethodThrowException(
            T object, Method method, Object[] parameterValues, Class<?>... groups) {

        final Set<ConstraintViolation<T>> constraintViolations = validateMethod(object, method, parameterValues, groups);
        if (CollectionUtils.isNotEmpty(constraintViolations)) {
            throw new ConstraintViolationException(constraintViolations);
        }
    }

    /**
     * 验证方法入参，包装异常信息
     *
     * @param <T>             所属对象的类型
     * @param object          方法所属对象
     * @param method          方法
     * @param parameterValues 方法入参值
     * @param groups          校验组
     * @return {@link #wrapConstraintViolations(Set)}
     */
    public static <T> List<String> validateMethodWrapConstraintViolations(
            T object, Method method, Object[] parameterValues, Class<?>... groups) {

        final Set<ConstraintViolation<T>> constraintViolations = validateMethod(object, method, parameterValues, groups);
        return wrapConstraintViolations(constraintViolations);
    }

    /**
     * 包装验证每个字段的异常信息，以field:error_message形式展示
     *
     * @param constraintViolations 违反的约束信息
     * @return {@link List<String> }
     */
    public static <T> List<String> wrapConstraintViolations(
            Set<ConstraintViolation<T>> constraintViolations) {

        return constraintViolations.stream()
                .map(violation -> violation.getPropertyPath() + ":" + violation.getMessage())
                .collect(Collectors.toList());
    }

}